﻿using System.Collections.Generic;
using System.Linq;
using Game2DFramework;
using Game2DFramework.Collision;
using Game2DFramework.Extensions;
using MechGame.Data;
using MechGame.Game.PlayerRobot;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace MechGame.Game.Leveling
{
    class Level : GameObject
    {
        private readonly Player _player;
        private readonly Tilemap _tilemap;
        private readonly SavePoint _savePoint;
        private readonly List<Box> _boxes;
        private readonly List<Gate> _gates; 
        private readonly List<Item> _items;
        private readonly Dictionary<AttachedItem, Texture2D> _textureOfItemType;

        public LevelState State { get; private set; }
        public bool IsDarkRoom { get; private set; }

        public Level(Game2D game, string levelAsset) : base(game)
        {
            _player = Game.GetGlobalObject<Player>("Player");
            _textureOfItemType = new Dictionary<AttachedItem, Texture2D>
            {
                {AttachedItem.EnergyContainer, Game.Content.Load<Texture2D>("textures/energycontainer")}
            };

            var levelData = Game.Content.Load<LevelData>(levelAsset);
            _tilemap = new Tilemap(Game.Content, levelData.TilemapAsset);

            if (levelData.HaveSavePoint)
            {
                _savePoint = new SavePoint(Game.Content)
                                 {
                                     Position = _tilemap.GetWorldFromTilePosition(levelData.SavePointPosition)
                                 };
                _savePoint.CatchPosition = _savePoint.Position + new Vector2(32, 32);
            }

            _boxes = new List<Box>();
            foreach (var boxPosition in levelData.BoxPositions)
            {
                var box = new Box(Game, AttachedItem.EnergyContainer) { Position = boxPosition.ToVector2() };
                _boxes.Add(box);
            }

            _gates = new List<Gate>();
            foreach (var gateData in levelData.Gates)
            {
                var gate = new Gate(Game.Content, gateData.Level)
                               {
                                   Position = _tilemap.GetWorldFromTilePosition(gateData.Position),
                               };
                _gates.Add(gate);
            }

            _items = new List<Item>();

            State = LevelState.Normal;
            IsDarkRoom = levelData.IsDarkRoom;
        }

        public Rectangle WorldBounds 
        {
            get { return _tilemap.WorldBounds; }
        }

        public Vector2 GetPlayerStartup()
        {
            return _tilemap.GetWorldFromTilePositionCentered(new Point(10, 7));
        }

        public void StartPlayerInCatchedMode(Player player)
        {
            _savePoint.SetInCatchedMode(player);
            _savePoint.ReleasePlayer();
        }

        public LevelChangeInformation Update(float elapsed)
        {
            State = LevelState.Normal;
            
            UpdateSavePoint(elapsed);
            UpdateBoxes();
            
            _items.ForEach(i => i.Update(elapsed));
            _gates.ForEach(g => g.Update(elapsed, _player));

            return _gates.Any(g => g.IsPlayerCatched) 
                ? LevelChangeInformation.FromGate("Levels/level2", new Vector2(200, 100)) 
                : LevelChangeInformation.Empty;
        }

        private void UpdateSavePoint(float elapsed)
        {
            if (_savePoint == null) return;
            if(_savePoint.State == DoorState.Closed && _savePoint.IsPlayerCatched) State = LevelState.PlayerInSaveBase;
            _savePoint.Update(elapsed, _player);
        }

        private void UpdateBoxes()
        {
            _boxes.RemoveAll(b => b.Deleted);
        }

        public void DropItem(AttachedItem itemType, Vector2 position)
        {
            _items.Add(new Item(_textureOfItemType[itemType], position, itemType));
        }

        public void LeaveSaveBase()
        {
            _savePoint.ReleasePlayer();
        }

        public void Draw()
        {
            _tilemap.Draw(Game.SpriteBatch);
            _items.ForEach(i => i.Draw(Game.SpriteBatch));
            _boxes.ForEach(b => b.Draw(Game.SpriteBatch));
            _gates.ForEach(g => g.Draw(Game.SpriteBatch));

            if (_savePoint == null)
            {
                _player.Draw();
            }
            else
            {
                if (_savePoint.IsPlayerCatched) _player.Draw();
                _savePoint.Draw(Game.SpriteBatch);
                if (!_savePoint.IsPlayerCatched) _player.Draw();
            }
        }

        public bool TryGetCollidingObject(Circle bounds, out GameObject gameObject)
        {
            if (_tilemap.CollidesWith(bounds))
            {
                gameObject = this;
            }
            else
            {
                gameObject = _boxes.Where(b => !b.Deleted).FirstOrDefault(box => bounds.Intersects(box.Bounds));
            }
            return gameObject != null;
        }

        public bool CollidesWith(Circle bounds)
        {
            return _tilemap.CollidesWith(bounds);
        }

        public bool TryCollectionItem(Circle circle, out Item item)
        {
            item = _items.FirstOrDefault(i => i.Bounds.Intersects(circle));
            if (_items != null) _items.Remove(item);
            return item != null;
        }

        public bool CurrentyOnDestroyable(Vector2 position)
        {
            if (_boxes.Any(b => !b.Deleted && b.Bounds.Contains(position.ToPoint()))) return true;
            return false;
        }
    }
}
